@@ -2,4 +2,16 @@ |
||
2 | 2 |
<Bucket |
3 | 3 |
type = "1" |
4 | 4 |
version = "2.0"> |
5 |
+ <Breakpoints> |
|
6 |
+ <BreakpointProxy |
|
7 |
+ BreakpointExtensionID = "Xcode.Breakpoint.ExceptionBreakpoint"> |
|
8 |
+ <BreakpointContent |
|
9 |
+ shouldBeEnabled = "Yes" |
|
10 |
+ ignoreCount = "0" |
|
11 |
+ continueAfterRunningActions = "No" |
|
12 |
+ scope = "0" |
|
13 |
+ stopOnStyle = "0"> |
|
14 |
+ </BreakpointContent> |
|
15 |
+ </BreakpointProxy> |
|
16 |
+ </Breakpoints> |
|
5 | 17 |
</Bucket> |
@@ -73,7 +73,6 @@ class NetworkApi { |
||
73 | 73 |
if status == 200 { |
74 | 74 |
observer(.success(data)) |
75 | 75 |
} else { |
76 |
- print(json) |
|
77 | 76 |
Toast.show(message: (json["description"] as? String) ?? "") |
78 | 77 |
observer(.error(BusinessError(id: status))) |
79 | 78 |
} |
@@ -95,7 +94,6 @@ class NetworkApi { |
||
95 | 94 |
.responseJSON { (res) in |
96 | 95 |
switch res.result { |
97 | 96 |
case .success(let json): |
98 |
- print(json) |
|
99 | 97 |
guard let json = json as? [String: AnyObject], |
100 | 98 |
let data = resource.parse(json) else { |
101 | 99 |
observer(.error(ParseError())) |
@@ -23,7 +23,7 @@ struct WXUserInfoRemoteAPI: UserInfoRemoteAPI { |
||
23 | 23 |
let photoPath = "https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTJibSYLgvXpMakvD9FaCqfiaWqcMiaiaz905YxWPuO4hy8F2lGheV7kVr9vKKXFgmL1S5s4QJgxwuwtVw/132" //swiftlint:disable:this line_length |
24 | 24 |
return Single.create(subscribe: { (observer) in |
25 | 25 |
observer(.success(UserInfo(json: ["user_id": "fiDz2Ms" as AnyObject, |
26 |
- "userName": "郑剑飞" as AnyObject, |
|
26 |
+ "nickname": "郑剑飞" as AnyObject, |
|
27 | 27 |
"photoPath": photoPath as AnyObject]))) |
28 | 28 |
return Disposables.create() |
29 | 29 |
}) |
@@ -39,9 +39,9 @@ public class HomeViewModel { |
||
39 | 39 |
return _hasMoreData.asObservable() |
40 | 40 |
} |
41 | 41 |
|
42 |
- public var contents: Observable<[AnimatableSectionModel<Int, PhotoItem>]> { |
|
42 |
+ public var contents: Observable<[AnimatableSectionModel<String, PhotoItem>]> { |
|
43 | 43 |
return _items.map({ model in |
44 |
- return [AnimatableSectionModel(model: 0, items: model)] |
|
44 |
+ return [AnimatableSectionModel(model: "photoItem", items: model)] |
|
45 | 45 |
}) |
46 | 46 |
} |
47 | 47 |
|
@@ -88,6 +88,7 @@ public class HomeViewModel { |
||
88 | 88 |
self._items.accept(self._items.value + result.data) |
89 | 89 |
}, onError: {[weak self] (_) in |
90 | 90 |
guard let `self` = self else { return } |
91 |
+ self.page -= 1 |
|
91 | 92 |
self._isLoading.onNext(false) |
92 | 93 |
}).disposed(by: disposeBag) |
93 | 94 |
} |
@@ -97,9 +98,13 @@ public class HomeViewModel { |
||
97 | 98 |
let width = item.photo_thumbnail_w |
98 | 99 |
let height = item.photo_thumbnail_h |
99 | 100 |
|
100 |
-// header 42, footer: 32 |
|
101 |
+ /// header 42, footer: 32 |
|
101 | 102 |
return CGSize(width: width, height: height + 74) |
102 | 103 |
} |
104 |
+ |
|
105 |
+ public func clear() { |
|
106 |
+ _items.accept([]) |
|
107 |
+ } |
|
103 | 108 |
|
104 | 109 |
private func setNotification() { |
105 | 110 |
NotificationCenter.default.rx.notification(.GroupItemsChanged).subscribe(onNext: { (notification) in |
@@ -138,14 +143,6 @@ public class HomeViewModel { |
||
138 | 143 |
|
139 | 144 |
//coordinator delegate |
140 | 145 |
extension HomeViewModel { |
141 |
- public func scanQR() { |
|
142 |
- delegate?.scanQR() |
|
143 |
- } |
|
144 |
- |
|
145 |
- public func createGroup() { |
|
146 |
- delegate?.createGroup() |
|
147 |
- } |
|
148 |
- |
|
149 | 146 |
public func didSelect(_ currIndex: Int) { |
150 | 147 |
delegate?.didSelect(_items.value, currIndex: currIndex) |
151 | 148 |
} |
@@ -67,4 +67,10 @@ public class MessageViewModel { |
||
67 | 67 |
} |
68 | 68 |
}).disposed(by: disposeBag) |
69 | 69 |
} |
70 |
+ |
|
71 |
+ public func clear() { |
|
72 |
+ sysReadedTip.accept(false) |
|
73 |
+ thumbupReadedTip.accept(false) |
|
74 |
+ commentReadedTip.accept(false) |
|
75 |
+ } |
|
70 | 76 |
} |
@@ -18,6 +18,7 @@ public class UserInfoViewModel { |
||
18 | 18 |
|
19 | 19 |
public var isLoggedIn: Observable<Void> { |
20 | 20 |
return shareUserInfo.asObservable() |
21 |
+ .distinctUntilChanged({ $0.userId == $1.userId }) |
|
21 | 22 |
.filter { $0.userId != "" } |
22 | 23 |
.flatMapLatest { _ in |
23 | 24 |
return Observable.just(()) |
@@ -48,8 +49,10 @@ public class UserInfoViewModel { |
||
48 | 49 |
guestRemoteAPI: GuestUserInfoRemoteAPI(), |
49 | 50 |
wxRemoteAPI: WXUserInfoRemoteAPI()) |
50 | 51 |
|
51 |
- shareUserInfo.asObservable().subscribe(onNext: { (userInfo) in |
|
52 |
- ShareUserId = userInfo.userId |
|
52 |
+ shareUserInfo.asObservable() |
|
53 |
+ .distinctUntilChanged({ $0.userId == $1.userId }) |
|
54 |
+ .subscribe(onNext: { (userInfo) in |
|
55 |
+ ShareUserId = userInfo.userId |
|
53 | 56 |
}).disposed(by: disposeBag) |
54 | 57 |
|
55 | 58 |
guestLoginBtnTapped.subscribe {[unowned self] _ in |
@@ -12,10 +12,11 @@ public final class WaterfallFlowLayout: UICollectionViewLayout { |
||
12 | 12 |
|
13 | 13 |
private var minColumn: Int = 0 |
14 | 14 |
private var itemWidth: CGFloat = -1 |
15 |
- private var columnHeights = [CGFloat]() |
|
15 |
+ private var columnHeights: [CGFloat] = [] |
|
16 | 16 |
private var minColumnHeight: CGFloat = 0 |
17 | 17 |
private(set) var configuration = WaterfallFlowConfiguration() |
18 |
- private var attributesArr = [UICollectionViewLayoutAttributes]() |
|
18 |
+ private var attributesArr: [UICollectionViewLayoutAttributes] = [] |
|
19 |
+ private var isNeedLayout: Bool = true |
|
19 | 20 |
override public init() { |
20 | 21 |
super.init() |
21 | 22 |
} |
@@ -24,7 +25,7 @@ public final class WaterfallFlowLayout: UICollectionViewLayout { |
||
24 | 25 |
self.init() |
25 | 26 |
self.configuration = configuration |
26 | 27 |
} |
27 |
- |
|
28 |
+ |
|
28 | 29 |
required init?(coder aDecoder: NSCoder) { |
29 | 30 |
super.init(coder: aDecoder) |
30 | 31 |
} |
@@ -39,7 +40,7 @@ public final class WaterfallFlowLayout: UICollectionViewLayout { |
||
39 | 40 |
} |
40 | 41 |
|
41 | 42 |
override public func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { |
42 |
- guard attributesArr.count <= indexPath.row else { |
|
43 |
+ guard attributesArr.count <= indexPath.row, isNeedLayout else { |
|
43 | 44 |
return attributesArr[indexPath.row] |
44 | 45 |
} |
45 | 46 |
|
@@ -59,11 +60,10 @@ public final class WaterfallFlowLayout: UICollectionViewLayout { |
||
59 | 60 |
|
60 | 61 |
fileprivate func initialize() { |
61 | 62 |
guard collectionView?.numberOfSections == 1, |
62 |
- let itemCount = collectionView?.numberOfItems(inSection: 0), |
|
63 |
- itemCount != 0 else { return } |
|
63 |
+ let itemCount = collectionView?.numberOfItems(inSection: 0) else { return } |
|
64 | 64 |
|
65 | 65 |
let originIndex: Int |
66 |
- if attributesArr.count >= itemCount || itemWidth == -1 { |
|
66 |
+ if attributesArr.count != itemCount || itemCount == 0 || itemWidth == -1 || isNeedLayout { |
|
67 | 67 |
minColumn = 0 |
68 | 68 |
originIndex = 0 |
69 | 69 |
minColumnHeight = 0 |
@@ -73,6 +73,12 @@ public final class WaterfallFlowLayout: UICollectionViewLayout { |
||
73 | 73 |
} else { |
74 | 74 |
originIndex = attributesArr.count |
75 | 75 |
} |
76 |
+ |
|
77 |
+ if itemCount == 0 { |
|
78 |
+ columnHeights = [] |
|
79 |
+ return |
|
80 |
+ } |
|
81 |
+ |
|
76 | 82 |
for i in originIndex..<itemCount { |
77 | 83 |
guard let attributes = layoutAttributesForItem(at: IndexPath(row: i, section: 0)) |
78 | 84 |
else { continue } |
@@ -81,9 +87,9 @@ public final class WaterfallFlowLayout: UICollectionViewLayout { |
||
81 | 87 |
} |
82 | 88 |
|
83 | 89 |
fileprivate func calculateViewSize() -> CGSize { |
84 |
- guard let collectionView = collectionView, |
|
90 |
+ guard let v = collectionView, |
|
85 | 91 |
let maxH = columnHeights.max() else { return CGSize.zero } |
86 |
- return CGSize(width: collectionView.bounds.width, height: maxH + configuration.rowSpace) |
|
92 |
+ return CGSize(width: v.bounds.width, height: maxH + configuration.rowSpace) |
|
87 | 93 |
} |
88 | 94 |
|
89 | 95 |
fileprivate func calculateItemX() -> CGFloat { |
@@ -117,4 +123,9 @@ public final class WaterfallFlowLayout: UICollectionViewLayout { |
||
117 | 123 |
columnHeights[minColumn] = minColumnHeight |
118 | 124 |
(minColumn, minColumnHeight) = columnHeights.enumerated().min(by: { $0.1 < $1.1 }) ?? (0, 0) |
119 | 125 |
} |
126 |
+ |
|
127 |
+ /// called at collectionView reload |
|
128 |
+ func setNeedsLayout() { |
|
129 |
+ isNeedLayout = true |
|
130 |
+ } |
|
120 | 131 |
} |
@@ -41,9 +41,8 @@ public final class AppCoordinator: BaseCoordinator<Void> { |
||
41 | 41 |
extension AppCoordinator: ContainerViewControllerDelegate { |
42 | 42 |
func presentLogin() { |
43 | 43 |
let vc = makeLoginViewController() |
44 |
- coordinate(to: LoginCoordinator(vc, |
|
45 |
- rootViewController: containerViewController)) |
|
46 |
- .subscribe(onNext: { (_) in |
|
44 |
+ coordinate(to: LoginCoordinator(vc)) |
|
45 |
+ .subscribe(onNext: { _ in |
|
47 | 46 |
vc.removeFromParentAndView() |
48 | 47 |
}).disposed(by: disposeBag) |
49 | 48 |
navigationController.addFullScreen(childViewController: vc) |
@@ -58,7 +57,7 @@ extension AppCoordinator: ContainerViewControllerDelegate { |
||
58 | 57 |
} |
59 | 58 |
} |
60 | 59 |
|
61 |
-fileprivate extension AppCoordinator { |
|
60 |
+extension AppCoordinator { |
|
62 | 61 |
func makeContainerViewController() { |
63 | 62 |
containerViewController.userInfo = shareUserInfoViewModel |
64 | 63 |
containerViewController.delegate = self |
@@ -100,7 +99,7 @@ fileprivate extension AppCoordinator { |
||
100 | 99 |
} |
101 | 100 |
} |
102 | 101 |
|
103 |
-fileprivate extension AppCoordinator { |
|
102 |
+extension AppCoordinator { |
|
104 | 103 |
func makeMineViewController() -> MineViewController { |
105 | 104 |
let vc = MineViewController.instantiate() |
106 | 105 |
vc.userInfoViewModel = shareUserInfoViewModel |
@@ -63,6 +63,7 @@ final class GroupViewController: UIViewController { |
||
63 | 63 |
collectionView.register(UINib(nibName: "PhotoCell", |
64 | 64 |
bundle: Bundle(identifier: "com.Paiai-iOS")), |
65 | 65 |
forCellWithReuseIdentifier: "photoCell") |
66 |
+ collectionView.alwaysBounceVertical = true |
|
66 | 67 |
setup() |
67 | 68 |
binding() |
68 | 69 |
setupNavigationBar() |
@@ -36,7 +36,7 @@ final class HomeViewController: UIViewController { |
||
36 | 36 |
collectionView.register(UINib(nibName: "PhotoCell", |
37 | 37 |
bundle: Bundle(identifier: "com.Paiai-iOS")), |
38 | 38 |
forCellWithReuseIdentifier: "photoCell") |
39 |
- |
|
39 |
+ collectionView.alwaysBounceVertical = true |
|
40 | 40 |
setup() |
41 | 41 |
binding() |
42 | 42 |
} |
@@ -69,12 +69,12 @@ final class HomeViewController: UIViewController { |
||
69 | 69 |
/// UI bindings |
70 | 70 |
fileprivate extension HomeViewController { |
71 | 71 |
|
72 |
- var dataSource: RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<Int, PhotoItem>> { |
|
73 |
- return RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<Int, PhotoItem>>( |
|
72 |
+ var dataSource: RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<String, PhotoItem>> { |
|
73 |
+ return RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<String, PhotoItem>>( |
|
74 | 74 |
configureCell: {(_, collectionView, indexPath, item) -> UICollectionViewCell in |
75 |
- let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCell", |
|
76 |
- for: indexPath) as! PhotoCell |
|
77 |
- cell.setInfo(item, source: .home) |
|
75 |
+ let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCell", |
|
76 |
+ for: indexPath) as! PhotoCell |
|
77 |
+ cell.setInfo(item, source: .home) |
|
78 | 78 |
return cell |
79 | 79 |
}) |
80 | 80 |
} |
@@ -111,14 +111,16 @@ fileprivate extension HomeViewController { |
||
111 | 111 |
func bindUserInfoViewModelToView() { |
112 | 112 |
userInfoViewModel.isLoggedIn |
113 | 113 |
.asDriver(onErrorJustReturn: ()) |
114 |
- .drive(onNext: { (_) in |
|
114 |
+ .drive(onNext: {[unowned self] _ in |
|
115 |
+ self.viewModel.clear() |
|
115 | 116 |
self.collectionView.startRefreshing(at: .top) |
116 | 117 |
}).disposed(by: disposeBag) |
117 | 118 |
} |
118 | 119 |
|
119 | 120 |
func bindViewModelToCollectionView() { |
120 | 121 |
viewModel.contents |
121 |
- .bind(to: collectionView.rx.items(dataSource: dataSource)) |
|
122 |
+ .asDriver(onErrorDriveWith: .empty()) |
|
123 |
+ .drive(collectionView.rx.items(dataSource: dataSource)) |
|
122 | 124 |
.disposed(by: disposeBag) |
123 | 125 |
} |
124 | 126 |
|
@@ -10,21 +10,17 @@ import UIKit |
||
10 | 10 |
import RxSwift |
11 | 11 |
import PaiaiDataKit |
12 | 12 |
|
13 |
-class LoginCoordinator: BaseCoordinator<UserInfo> { |
|
14 |
- private let rootViewController: UIViewController |
|
13 |
+class LoginCoordinator: BaseCoordinator<Void> { |
|
15 | 14 |
private let loginViewController: LoginViewController |
16 | 15 |
|
17 |
- init(_ loginViewController: LoginViewController, |
|
18 |
- rootViewController: UIViewController) { |
|
19 |
- self.rootViewController = rootViewController |
|
16 |
+ init(_ loginViewController: LoginViewController) { |
|
20 | 17 |
self.loginViewController = loginViewController |
21 | 18 |
|
22 | 19 |
super.init(navigationController: UINavigationController(), viewController: loginViewController) |
23 | 20 |
} |
24 | 21 |
|
25 |
- override func start() -> Observable<UserInfo> { |
|
26 |
- let viewModel = UserInfoViewModel() |
|
27 |
- loginViewController.userInfoViewModel = viewModel |
|
28 |
- return viewModel.shareUserInfo.asObservable() |
|
22 |
+ override func start() -> Observable<Void> { |
|
23 |
+ |
|
24 |
+ return loginViewController.userInfoViewModel.loginCompleted |
|
29 | 25 |
} |
30 | 26 |
} |
@@ -39,7 +39,6 @@ extension LoginViewController { |
||
39 | 39 |
func binding() { |
40 | 40 |
bindInteraction() |
41 | 41 |
bindScrollViewDelegate() |
42 |
- bindUserInfoViewModelToView() |
|
43 | 42 |
} |
44 | 43 |
|
45 | 44 |
func bindInteraction() { |
@@ -52,12 +51,6 @@ extension LoginViewController { |
||
52 | 51 |
.disposed(by: disposeBag) |
53 | 52 |
} |
54 | 53 |
|
55 |
- func bindUserInfoViewModelToView() { |
|
56 |
- userInfoViewModel.loginCompleted.subscribe {[unowned self] _ in |
|
57 |
- self.removeFromParentAndView() |
|
58 |
- }.disposed(by: disposeBag) |
|
59 |
- } |
|
60 |
- |
|
61 | 54 |
func bindScrollViewDelegate() { |
62 | 55 |
scrollView.rx.didEndDecelerating |
63 | 56 |
.asDriver() |
@@ -59,6 +59,7 @@ extension MessageViewController { |
||
59 | 59 |
userInfoViewModel.isLoggedIn |
60 | 60 |
.asDriver(onErrorJustReturn: ()) |
61 | 61 |
.drive(onNext: {[unowned self] (_) in |
62 |
+ self.viewModel.clear() |
|
62 | 63 |
self.viewModel.reload() |
63 | 64 |
}).disposed(by: disposeBag) |
64 | 65 |
} |
@@ -33,10 +33,15 @@ extension MineCoordinator: MineViewControllerDelegate { |
||
33 | 33 |
func logout() { |
34 | 34 |
mineViewController.dismissController() |
35 | 35 |
didCancel.onNext(()) |
36 |
- |
|
37 |
- let vc = makeLoginViewController() |
|
38 |
- vc.userInfoViewModel = mineViewController.userInfoViewModel |
|
39 |
- navigationController.addFullScreen(childViewController: vc) |
|
36 |
+ |
|
37 |
+ guard let coordinator = parentCoordinator as? AppCoordinator else { return } |
|
38 |
+ |
|
39 |
+ let vc = coordinator.makeLoginViewController() |
|
40 |
+ let loginCoordinator = LoginCoordinator(vc) |
|
41 |
+ coordinator.coordinate(to: loginCoordinator).subscribe { _ in |
|
42 |
+ vc.dismissController() |
|
43 |
+ }.disposed(by: disposeBag) |
|
44 |
+ navigationController.presentController(vc) |
|
40 | 45 |
} |
41 | 46 |
|
42 | 47 |
func didSelect(_ item: MineItem) { |
@@ -58,9 +63,6 @@ extension MineCoordinator: MineViewControllerDelegate { |
||
58 | 63 |
self.didCancel.onNext(()) |
59 | 64 |
}).disposed(by: disposeBag) |
60 | 65 |
} |
61 |
- |
|
62 |
- func loginout() { |
|
63 |
- } |
|
64 | 66 |
} |
65 | 67 |
|
66 | 68 |
extension MineCoordinator: MineGroupViewModelDelegate { |
@@ -74,12 +76,6 @@ extension MineCoordinator: MineGroupViewModelDelegate { |
||
74 | 76 |
} |
75 | 77 |
|
76 | 78 |
fileprivate extension MineCoordinator { |
77 |
- func makeLoginViewController() -> LoginViewController { |
|
78 |
- let vc = LoginViewController.instantiate() |
|
79 |
-// vc.userInfoViewModel = shareUserInfoViewModel |
|
80 |
- return vc |
|
81 |
- } |
|
82 |
- |
|
83 | 79 |
func makeMineGroupViewController() -> MineGroupViewController { |
84 | 80 |
let vc = MineGroupViewController.instantiate() |
85 | 81 |
vc.viewModel = MineGroupViewModel() |
@@ -20,6 +20,7 @@ public class BaseCoordinator<ResultType> { |
||
20 | 20 |
|
21 | 21 |
private let identifier = UUID() |
22 | 22 |
private var childCoordinators = [UUID: Any]() |
23 |
+ var parentCoordinator: (Any)? = nil |
|
23 | 24 |
|
24 | 25 |
init(navigationController: UINavigationController, viewController: UIViewController) { |
25 | 26 |
self.viewController = viewController |
@@ -37,6 +38,7 @@ public class BaseCoordinator<ResultType> { |
||
37 | 38 |
|
38 | 39 |
func coordinate<T>(to coordinator: BaseCoordinator<T>) -> Observable<T> { |
39 | 40 |
store(coordinator: coordinator) |
41 |
+ coordinator.parentCoordinator = self |
|
40 | 42 |
return coordinator.start() |
41 | 43 |
.do(onNext: { [weak self] _ in |
42 | 44 |
self?.free(coordinator: coordinator) |